/*
 * Amadeus Confidential Information:
 * Unauthorized use and disclosure strictly forbidden.
 * @1998-2009 - Amadeus s.a.s - All Rights Reserved.
 */
package com.amadeus.spin.devtool.soap.shooter.config;

import java.io.File;
import java.io.IOException;

import org.apache.log4j.FileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.w3c.dom.Node;
import org.w3c.dom.traversal.NodeIterator;

import com.amadeus.spin.devtool.soap.shooter.AbortException;
import com.amadeus.spin.devtool.soap.shooter.FatalException;
import com.amadeus.spin.devtool.soap.shooter.Shooter;
import com.amadeus.spin.devtool.soap.shooter.filter.DefaultFilterInitializer;
import com.amadeus.spin.devtool.soap.shooter.filter.FilterHandlerImpl;
import com.amadeus.spin.devtool.soap.shooter.process.RetrieveHostTraces;
import com.amadeus.spin.standard.util.XPathHelper;
import com.amadeus.spin.util.soapshooter.ShooterException;
import com.amadeus.spin.util.soapshooter.SoapClient;

public abstract class InitializeShooter {

  public static final String BACKUP_TRACE_SUBDIR = "backupTraces";

  private static Logger logger = Logger.getLogger(InitializeShooter.class);

  private static final String XPATH_FILTER_OLD = "/scenario/filter";

  private static final String XPATH_FILTER = "/scenario/filters/filter";

  private static final String XPATH_GLOBAL_INPUT = "/scenario/global/input";

  private static final String XPATH_GLOBAL_OUTPUT = "/scenario/global/output";

  /**
   * Load the technical settings from the scenario configuration, if any. Parameter entered via the command line should
   * have precedence.
   *
   * @throws IOException
   * @throws FatalException
   * @throws AbortException
   */
  public static void initialize(Shooter shooter) throws FatalException, AbortException {
    if (ConfigurationParameters.getScenarioFileName() == null) {
      ConfigurationParameters.setScenarioFileName("scenario.xml");
    }
    initializeLogger();
    SoapshooterScenario scenarioConfiguration = null;
    scenarioConfiguration = new SoapshooterScenario(ConfigurationParameters.getScenarioFileName());
    initializeGivenConfiguration(shooter, scenarioConfiguration);
    shooter.setConf(scenarioConfiguration);
    // format directory parameters if needed;
    // provide some default values
    ConfigurationParameters.updateConfigurationParameters();
    initializeOuputDirStructure();
    Shooter.setShooter(initializeShooter());
  }

  /**
   * Reads the scenario to set its configuration.
   *
   * @param shooter
   * @param scenario
   * @throws FatalException
   */
  private static void initializeGivenConfiguration(Shooter shooter, SoapshooterScenario scenario) throws FatalException {
    if (ConfigurationParameters.getServer() == null ||
        ConfigurationParameters.getServer().trim().equalsIgnoreCase("null")) {
      ConfigurationParameters.setServer(scenario.getProperty("/scenario/settings/@server"));
    }
    if (ConfigurationParameters.getServerStreamName() == null) {
      String serverStreamName = scenario.getProperty("/scenario/settings/@stream");
      if (serverStreamName != null) {
        ConfigurationParameters.setServerStreamName(serverStreamName);
      }
    }
    // Add custom nodes to the scenario - according options passed to
    // soapshooter.
    customizeConfiguration(scenario);
    if (ConfigurationParameters.getScenarioFileName() == null) {
      ConfigurationParameters.setScenarioFileName(scenario.getProperty("/scenario/settings/@scenarioname"));
      if (ConfigurationParameters.getScenarioFileName() == null) {
        throw new FatalException("initialization with a configuration object and no scenario name provided.");
      }
    }
    if (ConfigurationParameters.getFactory() == null) {
      ConfigurationParameters.setFactory(new Factory());
    }
    if (ConfigurationParameters.getRouter() == null) {
      ConfigurationParameters.setRouter(scenario.getProperty("/scenario/settings/@router"));
    }
    if (ConfigurationParameters.getComment() == null ||
        ConfigurationParameters.getComment().trim().equalsIgnoreCase("")) {
      ConfigurationParameters.setComment(scenario.getProperty("/scenario/comment"));
    }
    ConfigurationParameters.setInputDir(scenario.getProperty("/scenario/settings/@in"));
    if (ConfigurationParameters.getOutputDir() == null) {
      ConfigurationParameters.setOutputDir(scenario.getProperty("/scenario/settings/@out"));
    }
    // CSC added an extra directory, for logs to be written to a file as
    // well.
    if (ConfigurationParameters.getTraceDir() == null) {
      ConfigurationParameters.setTraceDir(scenario.getProperty("/scenario/settings/@soaptrace"));
    }
    // We do not want verbose mode to be set in the file
    if (ConfigurationParameters.getUsername() == null) {
      ConfigurationParameters.setUsername(scenario.getProperty("/scenario/settings/@username"));
    }
    if (ConfigurationParameters.getPassword() == null) {
      ConfigurationParameters.setPassword(scenario.getProperty("/scenario/settings/@password"));
    }
    if (ConfigurationParameters.getServiceid() == null) {
      ConfigurationParameters.setServiceid(scenario.getProperty("/scenario/settings/@serviceid"));
    }
    if (ConfigurationParameters.getProxyhost() == null && ConfigurationParameters.getProxyport() <= 0) {
      String proxyData = scenario.getProperty("/scenario/settings/@proxy");
      if (proxyData != null) {
        int pos = proxyData.indexOf(':');
        if (pos != -1) {
          try {
            ConfigurationParameters.setProxyport(Integer.parseInt(proxyData.substring(pos + 1)));
            ConfigurationParameters.setProxyhost(proxyData.substring(0, pos));
          }
          catch (NumberFormatException nfe) {
            logger.error(nfe.getMessage());
            ConfigurationParameters.setProxyport(-1);
          }
        }
      }
    }
    if (ConfigurationParameters.getProxyUserName() == null) {
      ConfigurationParameters.setProxyUserName(scenario.getProperty("/scenario/settings/@proxyusername"));
    }
    if (ConfigurationParameters.getProxyPassword() == null) {
      ConfigurationParameters.setProxyPassword(scenario.getProperty("/scenario/settings/@proxypassword"));
    }
    if (ConfigurationParameters.getSessionId() == null) {
      ConfigurationParameters.setSessionId(scenario.getProperty("/scenario/settings/@sessionid"));
    }
    if (!ConfigurationParameters.getURLRewriting()) {
      String urlRewriting = scenario.getProperty("/scenario/settings/@urlrewriting");
      if ("true".equalsIgnoreCase(urlRewriting)) {
        ConfigurationParameters.setURLRewriting(true);
      }
    }

    initFilters(scenario);
  }

  /**
   * Reads the scenario filters and loads them
   *
   * @param scenario
   * @throws FatalException
   */
  private static void initFilters(SoapshooterScenario scenario) throws FatalException {
    /** Filters management */
    try {
      NodeIterator filterIterator = scenario.getNodeIterator(XPATH_FILTER);
      NodeIterator filterIteratorOld = scenario.getNodeIterator(XPATH_FILTER_OLD);
      ConfigurationParameters.setFilterHandler(new FilterHandlerImpl());
      DefaultFilterInitializer.setDefaultFilters(scenario);

      Node filter = filterIterator.nextNode();
      if (filter != null && filterIteratorOld.nextNode() != null) {
        logger.warn("filters defined at xPath " + XPATH_FILTER + " and " + XPATH_FILTER_OLD);
        logger.warn("keeping filters defined in " + XPATH_FILTER + ", discarding the others.");
      }
      else {
        filterIterator = filterIteratorOld;
        filter = filterIteratorOld.nextNode();
      }

      for (; filter != null; filter = filterIterator.nextNode()) {
        ConfigurationParameters.getFilterHandler().addFilter(XPathHelper.getProperty(filter, "@name"),
            XPathHelper.getProperty(filter, "@type"), XPathHelper.getProperty(filter, "@value"));
      }
      // Add the command line filters after the usual ones in order to override them
      ConfigurationParameters.getFilterHandler().replaceFilters(ConfigurationParameters.getOptionFilters());
    }
    catch (SoapShooterInitializationException e) {
      throw new FatalException("Problem while processing filter", e);
    }
  }

  /**
   * Customization of the scenario configuration. Adds "process" nodes. - retrivehosttraces - replace variable (for
   * ghostmode)
   */
  static void customizeConfiguration(SoapshooterScenario scenario) throws FatalException {
    try {
      Node globalInputServiceList = scenario.getNode(XPATH_GLOBAL_INPUT);
      Node globalOutputServiceList = scenario.getNode(XPATH_GLOBAL_OUTPUT);
      if (globalInputServiceList != null && globalOutputServiceList != null &&
          ConfigurationParameters.isRetrieveHostTraces() && (ConfigurationParameters.getServer() != null &&
          ConfigurationParameters.getServer().indexOf("localhost") != -1)) {
        // Adding host trace retrieval service when shooting on
        // localhost.
        String serverLogLocation = ConfigurationParameters.getHosttracesPath();
        File serverLogLocationFile;
        serverLogLocationFile = new File(serverLogLocation);
        if ((!serverLogLocationFile.exists()) || (!serverLogLocationFile.isDirectory())) {
          logger.warn("Hosttrace directory '" + serverLogLocation + "' does not exist.");
        }
        else {
          createRetrieveHostTracesService(globalInputServiceList, globalOutputServiceList, serverLogLocation);
        }
      }
    }
    catch (Exception exc) {
      logger.error(exc.getMessage());
      throw new FatalException("Could not customize the soap scenario configuration here!", exc);
    }
  }

  private static void createRetrieveHostTracesService(Node globalInputServiceList, Node globalOutputServiceList,
      String serverLogLocation) {
    appendRetrieveHostTraceService(globalInputServiceList, serverLogLocation);
    appendRetrieveHostTraceService(globalOutputServiceList, serverLogLocation);
  }

  private static void appendRetrieveHostTraceService(Node globalInputServiceList, String serverLogLocation) {
    globalInputServiceList.appendChild(RetrieveHostTraces.createRetrieveHostTracesServiceNode(
        globalInputServiceList.getOwnerDocument(), serverLogLocation, BACKUP_TRACE_SUBDIR));
  }

  /**
   * Creates or cleans the directory (= deletes all the files)
   *
   * @param aDirectoryFilePath
   */
  private static void createOrCleanDirectory(String aDirectoryFilePath) {
    File aDirectoryFile = new File(aDirectoryFilePath);
    if (!aDirectoryFile.exists()) {
      aDirectoryFile.mkdirs();
    }
    else {
      // We'd better clean the output directory here
      File[] fileArray = aDirectoryFile.listFiles();
      for (int fileIndex = 0; fileIndex < fileArray.length; fileIndex++) {
        if (!fileArray[fileIndex].isDirectory()) {
          long timeSinceLastModificationInMS = System.currentTimeMillis() -
              fileArray[fileIndex].lastModified();
          if (timeSinceLastModificationInMS > ConfigurationParameters.getTimeOfValidityInMS()) {
            fileArray[fileIndex].delete();
          }
        }
      }
    }
  }

  private static void initializeLogger() {
    FileAppender appender = (FileAppender)logger.getLoggerRepository().getRootLogger().getAppender("logfile");
    if (ConfigurationParameters.isDumpSoapTrace()) {
      appender.setFile(ConfigurationParameters.getReportFilePath());
      appender.activateOptions();
    }
    else {
      appender.setThreshold(Level.OFF);
    }
  }

  private static void initializeOuputDirStructure() throws FatalException {
    String inputDir = ConfigurationParameters.getInputDir();
    String outputDir = ConfigurationParameters.getOutputDir();
    String traceDir = ConfigurationParameters.getTraceDir();
    if (inputDir == null || outputDir == null || traceDir == null) {
      throw new FatalException(
          "Scenario cannot be launched: mandatory parameters 'in', 'out' or 'soaptrace' were not provided");
    }
    // Making the output directory structure, if needed; clean it
    // otherwise
    // - Output directory, for 'result_*' files to be saved
    if (ConfigurationParameters.isDumpOutput()) {
      createOrCleanDirectory(outputDir);
      // - Hosttrace output subdirectory (crappy way to set it anytime...)
      if (ConfigurationParameters.isRetrieveHostTraces()) {
        String hosttraceDir = outputDir + "/" + RetrieveHostTraces.OUTPUT_TRACES_SUBDIR;
        createOrCleanDirectory(hosttraceDir);
        createOrCleanDirectory(hosttraceDir + "/" + RetrieveHostTraces.A2C_ULTIMATE_SUBDIR);
      }
    }
  }

  /**
   * Given the configuration for the soap scenario, instanciate the shooter with the connexion parameters parsed.
   * Possible types of shooters: Apache-Soap, Axis.
   *
   * @throws FatalException
   */
  private static com.amadeus.spin.util.soapshooter.Shooter initializeShooter() throws FatalException {
    com.amadeus.spin.util.soapshooter.Shooter shooter = null;
    try {
      shooter = new SoapClient(ConfigurationParameters.getFullTargetPath());
      if (ConfigurationParameters.getUsername() != null) {
        ((SoapClient)shooter).setUserName(ConfigurationParameters.getUsername());
      }
      if (ConfigurationParameters.isSessionLess()) {
        ((SoapClient)shooter).setSessionLess(true);
      }
      if (ConfigurationParameters.getPassword() != null) {
        ((SoapClient)shooter).setPassword(ConfigurationParameters.getPassword());
      }
      if (ConfigurationParameters.getServiceid() != null) {
        ((SoapClient)shooter).setUrn(ConfigurationParameters.getServiceid());
      }
      if ((ConfigurationParameters.getProxyhost() != null) && (ConfigurationParameters.getProxyport() > 0)) {
        ((SoapClient)shooter)
              .setProxy(ConfigurationParameters.getProxyhost(), ConfigurationParameters.getProxyport());
      }
      if (ConfigurationParameters.getProxyUserName() != null) {
        ((SoapClient)shooter).setProxyUserName(ConfigurationParameters.getProxyUserName());
      }
      if (ConfigurationParameters.getProxyPassword() != null) {
        ((SoapClient)shooter).setProxyPassword(ConfigurationParameters.getProxyPassword());
      }
      // set timeout
      if (ConfigurationParameters.getTimeout() >= 0) {
        ((SoapClient)shooter).setTimeout(ConfigurationParameters.getTimeout());
      }
    }
    catch (ShooterException e) {
      throw new FatalException("Soap configuration issue", e);
    }
    return shooter;
  }
}
